using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using System;

namespace IQIGame.Onigao.Game
{
    /// <summary>
    /// 计时器管理器，可以根据时间间隔（秒）设置帧数函数添加到管理器内，按照一定时间间隔调用，适合一些没有继承MonoBehavior但却需要帧刷的类
    /// </summary>
    public class TimerManager : Singleton<TimerManager>
    {

        protected uint _idGenerator = 0;
        private BetterLinearMap<Counter> _timers;
        private BetterList<uint, Counter> _timersToAdd;

        /// <summary>
        /// 逻辑帧序号
        /// </summary>
        private ulong _frame = 0;

        private TimerManager()
        {
            if (FrameworkEntry.Instance == null)
            {
                LogGame.LogError("TimerManager的初始化（调用）时机应该在FrameworkEntry.Instance.Init()之后");
                return;
            }
            _timers = new BetterLinearMap<Counter>();
            _timersToAdd = new BetterList<uint, Counter>();
            FrameworkEntry.MonoDriver.updateEvent += OnUpdate;
            FrameworkEntry.MonoDriver.lateUpdateEvent += OnLateUpdate;
        }

        /// <summary>
        /// 生成新id
        /// </summary>
        /// <returns></returns>
        protected virtual uint GenTimerId()
        {
            do
            {
                _idGenerator++;
            }
            while (_idGenerator == 0 || _timers.ContainId(_idGenerator));
            return _idGenerator;
        }

        /// <summary>
        /// 添加帧刷函数
        /// </summary>
        /// <param name="interval">计时间隔，单位取决于参数timerType，当timerType == TimerType.Frame时，单位为帧，否则为秒</param>
        /// <param name="handler">帧刷处理函数</param>
        /// <param name="loop">循环次数，默认为1</param>
        /// <param name="timerType">计时类型，默认为带缩放的时间，单位秒</param>
        /// <returns>timer id</returns>
        public uint StartTimer(Action handler, float interval, int loop = 1, TimerType timerType = TimerType.Time)
        {
            if (loop == 0)
            {
                LogGame.LogWarning("loop == 0，该计时器不会被执行。");
            }
            Counter timer = null;
            switch (timerType)
            {
                case TimerType.Frame:
                    timer = ClassPool.Get<FrameCounter>();
                    break;
                case TimerType.Time:
                    timer = ClassPool.Get<TimeCounter>();
                    break;
                case TimerType.UnscaledTime:
                    timer = ClassPool.Get<UnscaledTimeCounter>();
                    break;
                case TimerType.FixTime:
                    timer = ClassPool.Get<FixTimeCounter>();
                    break;
            }
            var id = GenTimerId();
            _timersToAdd.Add(id, timer);
            timer.Init(id, interval, handler, loop);

            return id;
        }

        /// <summary>
        /// 开始单次计时器
        /// </summary>
        /// <param name="interval"></param>
        /// <param name="timerType"></param>
        /// <returns></returns>
        public uint StartOneTimer(float interval, TimerType timerType = TimerType.Time)
        {
            return StartTimer(null, interval, 1, timerType);
        }

        /// <summary>
        /// 等待完成
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public UniTask AwaitTimer(uint id)
        {
            if (!_timersToAdd.TryGetValue(id, out var timer))
            {
                timer = _timers.GetValue(id);
            }
            if (timer != null)
            {
                return timer.AwaitCount();
            }
            return UniTask.CompletedTask;
        }

        /// <summary>
        /// 移除帧刷函数
        /// </summary>
        /// <param name="id"></param>
        /// <param name="invokeCallback"></param>
        public void StopTimer(uint id, bool invokeComplete = false)
        {
            var timer = _timers.GetValue(id);
            if (timer != null)
            {
                if (!timer.Expired)
                {
                    timer.Stop(invokeComplete);
                }
                return;
            }
            if (_timersToAdd.TryGetValue(id, out timer))
            {
                timer.Stop(invokeComplete);
                PutBack(timer);
                _timersToAdd.Remove(id);
            }
        }

        /// <summary>
        /// 移除所有计时器
        /// </summary>
        /// <param name="includeInfinite">是否移除无限循环计时器</param>
        //public void RemoveAllRender(bool includeInfinite)
        //{

        //    for (int i = _timers.Count - 1; i >= 0; i--)
        //    {
        //        Counter timer = _timers[i];
        //        if (timer.Loop > 0 || includeInfinite)
        //        {
        //            _timers.RemoveAt(i);
        //            PutBack(timer);
        //        }
        //    }
        //}

        private void OnUpdate(float deltaTime, float unscaledDeltaTime)
        {
            //先按添加顺序执行
            for (int i = 0; i < _timers.IndexCount; i++)
            {
                var timer = _timers.GetValueByIndex(i);
                if (timer != null)
                {
                    timer.OnUpdate(deltaTime, unscaledDeltaTime);
                }
            }
            //再统一移除失效的Timer
            for (int i = 0; i < _timers.IndexCount; i++)
            {
                var timer = _timers.GetValueByIndex(i);
                if (timer != null && timer.Expired)
                {
                    _timers.Remove(timer.id);
                    PutBack(timer);
                }
            }

            _frame++;
        }

        /// <summary>
        /// 需要在LateUpdate中添加
        /// </summary>
        private void OnLateUpdate()
        {
            if (_timersToAdd.Count > 0)
            {
                foreach (var timer in _timersToAdd)
                {
                    _timers.Add(timer.id, timer);
                }
                _timersToAdd.Clear();
            }

        }

        private void PutBack(Counter timer)
        {
            switch (timer)
            {
                case FrameCounter frameCounter:
                    ClassPool.Put(frameCounter);
                    break;
                case TimeCounter timeCounter:
                    ClassPool.Put(timeCounter);
                    break;
                case UnscaledTimeCounter unscaledTimeConter:
                    ClassPool.Put(unscaledTimeConter);
                    break;
                case FixTimeCounter fixTimeCounter:
                    ClassPool.Put(fixTimeCounter);
                    break;
            }
        }

        /// <summary>
        /// 当前帧
        /// </summary>
        public ulong CurrentFrame
        {
            get
            {
                return _frame;
            }
        }
    }
}
